/*
 * Copyright (C) 2011-2012 Wojciech Dzierżanowski
 * See LICENSE.txt for licensing details.
 */

package wdzierzan.downstream.android.servermanager;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.os.AsyncTask;
import android.text.InputType;
import android.text.method.PasswordTransformationMethod;
import android.widget.EditText;

import wdzierzan.downstream.android.Format;
import wdzierzan.downstream.android.R;
import wdzierzan.downstream.core.GstPipelineBuilder;
import wdzierzan.downstream.core.SshGstStreamer;
import wdzierzan.downstream.core.Streamer;

import java.io.IOException;


/**
 *
 * @author Wojciech Dzierżanowski <wojciech.dzierzanowski@gmail.com>
 */
class StreamerHolder {

    /**
     * A {@code Streamer} proxy that makes sure sshGstStreamer is marked for
     * reinitialization upon error.
     */
    private class StreamerProxy implements Streamer {
        public String[] listFiles(String path) throws IOException {
            try {
                return sshGstStreamer.listFiles(path);
            } catch (IOException e) {
                throw handleException(e);
            } catch (RuntimeException e) {
                throw handleException(e);
            }
        }

        public Streamer.FileType identify(String path) throws IOException {
            try {
                return sshGstStreamer.identify(path);
            } catch (IOException e) {
                throw handleException(e);
            } catch (RuntimeException e) {
                throw handleException(e);
            }
        }
        public void stream(String path, Streamer.FileType type) throws IOException {
            try {
                sshGstStreamer.stream(path, type);
            } catch (IOException e) {
                throw handleException(e);
            } catch (RuntimeException e) {
                throw handleException(e);
            }
        }

        private IOException handleException(IOException e) {
            disconnectStreamer();
            return e;
        }
        private RuntimeException handleException(RuntimeException e) {
            disconnectStreamer();
            return e;
        }
    }

    private boolean needsReconnect;
    private SshGstStreamer sshGstStreamer;
    private StreamerProxy proxy;
    private int streamerRefCount;

    /**
     * @see ServerManager#acquireStreamer()
     */
    Streamer getStreamer() {
        if (needsReconnect)
            return null;

        if (sshGstStreamer != null)
            ++streamerRefCount;

        return sshGstStreamer != null ? (proxy = new StreamerProxy()) : null;
    }

    void releaseStreamer(Streamer streamer) {
        if (streamer != proxy)
            return;

        if (streamerRefCount <= 0)
            throw new IllegalStateException("releaseStreamer() unmatched by acquireStreamer()");

        if (--streamerRefCount == 0) {
            this.sshGstStreamer.disconnect();
            this.sshGstStreamer = null;
        }
    }

    boolean hasReferences() {
        return streamerRefCount > 0;
    }

    void initializeStreamer(final String hostname, final int port,
            final String username, final Format format, final Context activityContext,
            final Runnable onSuccess, final Runnable onFailure,
            OnCancelListener onCancelListener) {

        Context appContext = activityContext.getApplicationContext();

        final EditText passwordView = new EditText(activityContext);
        passwordView.setSingleLine();
        passwordView.setTransformationMethod(new PasswordTransformationMethod());
        passwordView.setRawInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);

        new AlertDialog.Builder(activityContext)
                .setTitle(R.string.ask_password_title)
                .setMessage(appContext.getString(R.string.ask_password_message, username, hostname))
                .setView(passwordView)
                .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface di, int i) {
                            String password = passwordView.getText().toString();
                            new InitStreamerTask(hostname, port, username, format,
                                    activityContext, onSuccess, onFailure).execute(password);
                        }
                    })
                .setOnCancelListener(onCancelListener)
                .create().show();
    }

    void disconnectStreamer() {
        needsReconnect = true;
    }

    private class InitStreamerTask extends AsyncTask<String, Void, Boolean> {

        private final String hostname;
        private final int port;
        private final String username;
        private final Format format;
        private final Context context;
        private final Runnable onSuccess;
        private final Runnable onFailure;
        private Exception exception;

        public InitStreamerTask(String hostname, int port, String username, Format format,
                Context context, Runnable onSuccess, Runnable onFailure) {
            this.hostname = hostname;
            this.port = port;
            this.username = username;
            this.format = format;
            this.context = context;
            this.onSuccess = onSuccess;
            this.onFailure = onFailure;
        }

        @Override
        protected Boolean doInBackground(String... passwords) {
            exception = null;
            sshGstStreamer = new SshGstStreamer(hostname);
            try {
                if (sshGstStreamer.connect(username, passwords[0], 12 * 1000)) {
                    needsReconnect = false;
                    String myHostname = "`echo $SSH_CONNECTION | cut -f 1 -d \" \"`";

                    sshGstStreamer.setVerbatimPipeline(GstPipelineBuilder.via(myHostname, port));

                    switch (format) {
                        case OGG_VORBIS:
                            sshGstStreamer.setPipeline(GstPipelineBuilder.via(myHostname, port).toOggVorbis().quality(0.8));
                            break;
                        case MP3:
                            sshGstStreamer.setPipeline(GstPipelineBuilder.via(myHostname, port).toMp3().preset(1001));
                            break;
                        default:
                            assert false: "Unknown format";
                    }
                } else {
                    sshGstStreamer = null;
                }
            } catch (Exception e) {
                exception = e;
                sshGstStreamer = null;
            }
            return sshGstStreamer != null;
        }

        @Override
        protected void onPostExecute(Boolean connected) {
            if (connected) {
                assert(sshGstStreamer != null);
                onSuccess.run();
            } else {
                if (exception != null) {
                    new AlertDialog.Builder(context)
                            .setTitle(R.string.error)
                            .setMessage(exception.getMessage())
                            .create().show();
                }
                onFailure.run();
            }
        }
    }
}
